﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using UnityEngine;

public class StreamCapture : IStreamCapture {

    private IntPtr capture = IntPtr.Zero;
    public string url;
    private Action<StreamsParameters> openCb;
    private Action<InterruptCode> interruptCb;
    private GCHandle handle;
    private Frame[] frames, lastFrames;
    private int openIndex;
    public string MediaDescribe { private set; get; }
    public StreamCapture()
    {
        if (SCInstance.Instance == null)
            new GameObject("SCInstance").AddComponent<SCInstance>().Init();
        handle = GCHandle.Alloc(this);
        capture = CreateStreamCapture(GCHandle.ToIntPtr(handle));
        frames = new Frame[GetFrameTypeCount()];
        lastFrames = new Frame[GetFrameTypeCount()];
        for (int i = 0; i < frames.Length; i++)
        {
            frames[i] = new Frame();
            lastFrames[i] = new Frame();
        }
        
    }

    private int GetFrameTypeCount()
    {
        int num = 0;
        foreach (FrameType e in Enum.GetValues(typeof(FrameType)))
            ++num;
        return num;
    }
    public static void StreamCaptureLog(LogLevel level, string log)
    {
        switch (level)
        {
            case LogLevel.LEVEL_INFO:
                Debug.Log(log);
                break;
            case LogLevel.LEVEL_WARNING:
                Debug.LogWarning(log);
                break;
            default:
                Debug.LogWarning("Error:" + log);
                break;
        }
    }

    protected override void OpenCallback(int openIndex, StreamsParameters sp)
    {
        if (openCb == null || this.openIndex != openIndex) return;
        MediaDescribe = sp.describe;
        openCb(sp);
    }

    public void Open(Action<StreamsParameters> cb)
    {
        if (capture == IntPtr.Zero) return;
        openCb = cb;
        byte[] buffer = Encoding.UTF8.GetBytes(url);
        byte[] urlbuffer = new byte[buffer.Length + 1];
        Buffer.BlockCopy(buffer, 0, urlbuffer, 0, buffer.Length);
        //url buffer 要以/0结尾
        OpenAsync(capture, ++openIndex, urlbuffer, openPtr);
    }

    public void RegisterInterruptCallback(Action<InterruptCode> cb)
    {
        if (capture == IntPtr.Zero) return;
        RegisterInterruptCallback(capture, interruptPtr);
        interruptCb = cb;
    }

    protected override void InterruptCallback(InterruptCode interruptCode)
    {
        if (interruptCb != null)
            interruptCb(interruptCode);
    }
    public void Close()
    {
        MediaDescribe = "";
        if (capture == IntPtr.Zero) return;
        Close(capture);
        //Debug.LogWarning("Close StreamCapture");
    }

    public void Seek(double per)
    {
        if (capture == IntPtr.Zero) return;
        Seek(capture, per);
    }
    public double GetCurrentProgress()
    {
        if (capture == IntPtr.Zero) return 0;
        return GetCurrentProgress(capture);
    }

    public void SetAudioTrack(int index)
    {
        if (capture == IntPtr.Zero) return;
        SetAudioTrack(capture, index);
    }
    public PFCounts GetPFCounts()
    {
        if (capture == IntPtr.Zero) return null;
        IntPtr ptr = GetPFCounts(capture);
        if (ptr == IntPtr.Zero) return null;
        PFCounts counts = new PFCounts();
        ConvertPFCount(ref counts, ptr);
        return counts;
    }
    public Frame TryGrabFrame(FrameType frameType)
    {
        if (capture == IntPtr.Zero) return null;

        IntPtr framePtr = TryGrabFrame(capture, (int)frameType);
        if (framePtr == IntPtr.Zero) return null;
        ConvertFrame(ref frames[(int)frameType], framePtr);
        return frames[(int)frameType];
    }

    public Frame TryGrabLastFrame(FrameType frameType)
    {
        if (capture == IntPtr.Zero) return null;

        IntPtr framePtr = TryGrabLastFrame(capture, (int)frameType);
        if (framePtr == IntPtr.Zero) return null;
        ConvertFrame(ref lastFrames[(int)frameType], framePtr);
        return lastFrames[(int)frameType];
    }

    public void RemoveFrame(FrameType frameType)
    {
        if (capture == IntPtr.Zero) return;
        RemoveFrame(capture, (int)frameType);
    }
    public void Release()
    {
        if (capture == IntPtr.Zero) return;
        DeleteStreamCapture(capture);
        capture = IntPtr.Zero;
        handle.Free();
    }

    /// <summary>
    /// 设置选项，在打开之前更改有效
    /// </summary>
    /// <param name="type">要更改的选项</param>
    /// <param name="value">值</param>
    /// <returns>更改成功与否</returns>
    public bool SetOption(OptionType type, int value)
    {
        if (capture == IntPtr.Zero) return false;
        if (SetOption(capture, (int)type, value) == 0) return true;
        else return false;
    }

    public long GetDuration()
    {
        if (capture == IntPtr.Zero) return 0;
        return GetDuration(capture);
    }

    public CameraDescription GetCameraInfo(string cameraName)
    {
        byte[] buffer = Encoding.UTF8.GetBytes(cameraName);
        byte[] urlbuffer = new byte[buffer.Length + 1];
        Buffer.BlockCopy(buffer, 0, urlbuffer, 0, buffer.Length);
        //url buffer 要以/0结尾
        IntPtr ptr = GetCameraInfo(urlbuffer);
        CameraDescription cd = new CameraDescription();
        ConvertCameraDescription(ref cd, ptr);
        return cd;
    }

    ~StreamCapture()
    {
        Release();
    }
}
